<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien Ménager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace isszz\captcha\font\lib;

use isszz\captcha\font\lib\table\type\Name;
use isszz\captcha\font\lib\truetype\File;

/**
 * Adobe Font Metrics file creation utility class.
 *
 * @package php-font-lib
 */
class AdobeFontMetrics
{
	private $f;

	/**
	 * @var File
	 */
	private $font;

	public function __construct(File $font)
	{
		$this->font = $font;
	}

	public function write($file, $encoding = null)
	{
		$map_data = [];

		if ($encoding) {
			$encoding = preg_replace("/[^a-z0-9-_]/", "", $encoding);
			$map_file = dirname(__FILE__) . "/../maps/$encoding.map";
			if (!file_exists($map_file)) {
				throw new FontLibException("Unknown encoding ($encoding)");
			}

			$map      = new EncodingMap($map_file);
			$map_data = $map->parse();
		}

		$this->f = fopen($file, "w+");

		$font = $this->font;

		$this->startSection("FontMetrics", 4.1);
		$this->addPair("Notice", "Converted by PHP-font-lib");
		$this->addPair("Comment", "https://github.com/PhenX/php-font-lib");

		$encoding_scheme = ($encoding ? $encoding : "FontSpecific");
		$this->addPair("EncodingScheme", $encoding_scheme);

		$records = $font->getData("name", "records");
		foreach ($records as $id => $record) {
			if (!isset(Name::$nameIdCodes[$id]) || preg_match("/[\r\n]/", $record->string)) {
				continue;
			}

			$this->addPair(Name::$nameIdCodes[$id], $record->string);
		}

		$os2 = $font->getData("OS/2");
		$this->addPair("Weight", ($os2["usWeightClass"] > 400 ? "Bold" : "Medium"));

		$post = $font->getData("post");
		$this->addPair("ItalicAngle", $post["italicAngle"]);
		$this->addPair("IsFixedPitch", ($post["isFixedPitch"] ? "true" : "false"));
		$this->addPair("UnderlineThickness", $font->normalizeFUnit($post["underlineThickness"]));
		$this->addPair("UnderlinePosition", $font->normalizeFUnit($post["underlinePosition"]));

		$hhea = $font->getData("hhea");

		if (isset($hhea["ascent"])) {
			$this->addPair("FontHeightOffset", $font->normalizeFUnit($hhea["lineGap"]));
			$this->addPair("Ascender", $font->normalizeFUnit($hhea["ascent"]));
			$this->addPair("Descender", $font->normalizeFUnit($hhea["descent"]));
		}
		else {
			$this->addPair("FontHeightOffset", $font->normalizeFUnit($os2["typoLineGap"]));
			$this->addPair("Ascender", $font->normalizeFUnit($os2["typoAscender"]));
			$this->addPair("Descender", -abs($font->normalizeFUnit($os2["typoDescender"])));
		}

		$head = $font->getData("head");
		$this->addArray("FontBBox", [
			$font->normalizeFUnit($head["xMin"]),
			$font->normalizeFUnit($head["yMin"]),
			$font->normalizeFUnit($head["xMax"]),
			$font->normalizeFUnit($head["yMax"]),
		]);

		$glyphIndexArray = $font->getUnicodeCharMap();

		if ($glyphIndexArray) {
			$hmtx  = $font->getData("hmtx");
			$names = $font->getData("post", "names");

			$this->startSection("CharMetrics", count($hmtx));

			if ($encoding) {
				foreach ($map_data as $code => $value) {
					list($c, $name) = $value;

					if (!isset($glyphIndexArray[$c])) {
						continue;
					}

					$g = $glyphIndexArray[$c];

					if (!isset($hmtx[$g])) {
						$hmtx[$g] = $hmtx[0];
					}

					$this->addMetric([
						"C"  => ($code > 255 ? -1 : $code),
						"WX" => $font->normalizeFUnit($hmtx[$g][0]),
						"N"  => $name,
					]);
				}
			} else {
				foreach ($glyphIndexArray as $c => $g) {
					if (!isset($hmtx[$g])) {
						$hmtx[$g] = $hmtx[0];
					}

					$this->addMetric([
						"U"  => $c,
						"WX" => $font->normalizeFUnit($hmtx[$g][0]),
						"N"  => (isset($names[$g]) ? $names[$g] : sprintf("uni%04x", $c)),
						"G"  => $g,
					]);
				}
			}

			$this->endSection("CharMetrics");

			$kern = $font->getData("kern", "subtable");
			$tree = is_array($kern) ? $kern["tree"] : null;

			if (!$encoding && is_array($tree)) {
				$this->startSection("KernData");
				$this->startSection("KernPairs", count($tree, COUNT_RECURSIVE) - count($tree));

				foreach ($tree as $left => $values) {
					if (!is_array($values)) {
						continue;
					}
					if (!isset($glyphIndexArray[$left])) {
						continue;
					}

					$left_gid = $glyphIndexArray[$left];

					if (!isset($names[$left_gid])) {
						continue;
					}

					$left_name = $names[$left_gid];

					$this->addLine("");

					foreach ($values as $right => $value) {
						if (!isset($glyphIndexArray[$right])) {
							continue;
						}

						$right_gid = $glyphIndexArray[$right];

						if (!isset($names[$right_gid])) {
							continue;
						}

						$right_name = $names[$right_gid];
						$this->addPair("KPX", "$left_name $right_name $value");
					}
				}

				$this->endSection("KernPairs");
				$this->endSection("KernData");
			}
		}

		$this->endSection("FontMetrics");
	}

	public function addLine($line)
	{
		fwrite($this->f, "$line\n");
	}

	public function addPair($key, $value)
	{
		$this->addLine("$key $value");
	}

	public function addArray($key, $array) {
	$this->addLine("$key " . implode(" ", $array));
	}

	public function addMetric($data)
	{
		$array = [];
		foreach ($data as $key => $value) {
			$array[] = "$key $value";
		}
		$this->addLine(implode(" ; ", $array));
	}

	public function startSection($name, $value = "")
	{
		$this->addLine("Start$name $value");
	}

	public function endSection($name)
	{
		$this->addLine("End$name");
	}
}
